module qtrt

import dl
// import log
import vcp.vmm

/////////// vmm
pub fn real_malloc(c int) voidptr {
    return vmm.real_malloc(c)
}

///////////
pub fn keepme() {}

fn C.dlerror() byteptr
pub fn dlerror() string {
    cstr := C.dlerror()
    return tos_clone(cstr)
}

struct Rtglobs {
pub    mut:
    l &Log = vnil
    dlh voidptr = vnil
    dlhme voidptr = vnil
    dbg bool = false
    debugAllInheritsFilterFunc fn(string) bool
    debugIneritCall bool = false
    conns map[voidptr]map[string]&Qtconn // "sender" => signal => Qtconn

    inh &InheritHandler = newNullInheritHandler()
}

type Usize = size_t // foo
pub const vnil = voidptr(0)
const rtg = &Rtglobs{l: &Log{}}
// __global dlh = voidptr(0)
const qtmods = ["core", "gui", "widgets"]

fn init_rtg() {
    l := rtg.l
    l.set_level(Level.debug)
    mut rtv := rtg
    rtv.inh = newInheritHandler()
}

// only once
fn init() {
    init_rtg()
    init_dlme()
    init_dlso()

    init_callack_inherit()

    // mut l := rtg.l
    // rtg.l.debug(logpfx(@FILE, @LINE, @FN)+"init qtrt")
    debugln(@FILE, @LINE, "init qtrt")
}

// depend on vcp/builtin/vany.v
// usage: ldebug(@FILE, @LINE, @FN, "msg here")
fn debugln(file, line string, msgs...Any) {
    pfx := logpfxcom(file, line)
    msg := sprinta(msgs...)
    rtg.l.debug("${pfx} ${msg}")
}
pub fn infoln(file, line string, msgs...Any){
    pfx := logpfxcom(file, line)
    msg := sprinta(msgs...)
    rtg.l.info("${pfx} ${msg}")
}

const pathsep = '/'
// usage: logpfxcom(@FILE, @LINE)
fn logpfxcom(file, line string) string {
    mut pos2 := file.last_index(pathsep) or {0}
    pos2 = if pos2 > 0 { pos2 + 1} else {pos2}
    file2 := file[pos2..]
    return '$file2:$line:'
}

fn init_dlso() {
    assert rtg.dlh == voidptr(0)
    if rtg.dlh == voidptr(0) {
        mut sorawname := 'Qt5Inline'
        soname := 'lib${sorawname}.so' //soname := 'lib'+sorawname+'.so' // memory leak

        dlh := dl.open(soname, dl.rtld_now)
        if dlh == voidptr(0) {
            emsg := dlerror()
            panic("open soname error ${soname}: ${emsg}")
        }
        mut rtg2 := rtg
        rtg2.dlh = dlh
    }
    assert rtg.dlh != voidptr(0)
}

// fn C.dlopen(charptr, int) voidptr

fn init_dlme() {
    assert rtg.dlhme == voidptr(0)
    if rtg.dlhme == voidptr(0) {
        dlh := C.dlopen(vnil, dl.rtld_now)
        if dlh == voidptr(0) {
            emsg := dlerror()
            panic("open soname error me: ${emsg}")
        }
        mut rtg2 := rtg
        rtg2.dlhme = dlh
    }
    assert rtg.dlhme != voidptr(0)
}

pub fn sym_qtfunc6(crc32 u32, sym string) voidptr {
    // initdl()
    ptr := dl.sym(rtg.dlh, sym)
    if ptr == vnil {
        println("nil symbol addr, ${crc32}, ${sym}")
    }
    if crc32 != 0 {
        // cache it
    }
    return ptr
}
pub fn sym_cfunc6(crc32 u32, sym string) voidptr {
    // initdl()
    ptr := dl.sym(rtg.dlh, sym)
    if ptr == vnil {
        infoln(@FILE, @LINE, "not found", sym)
    }
    return ptr
}

pub fn sym_mefunc6(crc32 u32, sym string) voidptr {
    ptr := dl.sym(rtg.dlhme, sym)
    return ptr
}

pub fn invoke_qtfunc6(sym string, ffitype int, args...string ) (u64, int) {
    return 0,0
}
pub fn invoke_qtfunc7(sym string, ffitype int, args...voidptr ) (u64, int) {
    return 0,0
}

pub fn errprint(err int, rv u64) {
    if err != 0 {
        println("${err} - ${rv}")
    }
}
pub fn errpanic(err int, rv u64) {
}

pub fn vstringi(v i64) string { return tos2(byteptr(v)) }
pub fn vstringp(v voidptr) string {    return tos_clone(byteptr(v)) }
// just ref, not need manual free
pub fn cstring(s string) byteptr {    return s.str }
pub fn cstringr(s &string) byteptr {    return s.str }
pub fn cstrdup(s string) byteptr {    return memdup(s.str, s.len+1) }

pub fn cretval2v(typ string, v int) int {
    return 0
}

pub fn mallocgc(sz int) voidptr {
    return malloc(sz) // to v_malloc and then hook by vmm
    // return C.malloc(sz)
}
// must make sure raw malloc
pub fn mallocraw(sz int) voidptr {
    // track but not collect, should be use this
    return vmm.mallocuc(sz)
    // no track, no collect
    //return vmm.real_malloc(sz)
}

pub fn cmemset(mem voidptr, val int, sz int) {
    C.memset(mem, val, sz)
}
pub fn cmemcpy(mem voidptr, val voidptr, sz int) voidptr {
    return C.memcpy(mem, val, sz)
}
pub fn freemem(mem voidptr) {
    //C.free(mem)
}
pub fn free2(mem voidptr) { C.free(mem) }

pub fn string_slice_to_ccharpp(arr []string) voidptr {
    return voidptr(0)
}
pub fn ccharpp_to_string_slice(strpp voidptr) []string {
    return ["foo"]
}

type QtobjFinalFunc = fn(voidptr)

// old must be &GCRef
fn qtobj_finalizer(objref voidptr, cd voidptr) {
    fnname := @FN
    // C.printf("%s objref %p, cd %p\n", fnname.str, objref, cd)
    objref2 := &GCRef(objref)
    cthis := objref2.cthis
    finalfn := QtobjFinalFunc(cd)

    qtobj := CObject{cthis, objref2,0,0,0}
    finalfn(voidptr(&qtobj))
}
// obj must be CObject or QClass address
// the memory must layout as CObject
pub fn set_finalizer(obj voidptr, f fn(obj voidptr)) {
    //println(@FN)
    obj2 := &CObject(obj)
    objref := obj2.objref
    if objref == vnil {
        C.printf("wow objref %p\n", objref)
    }
    // cd is it self
    vmm.set_finalizer(obj2.objref, qtobj_finalizer, f)
}
pub fn release_owner_toqt(obj voidptr) {
    obj2 := &CObject(obj)
    // cd is it self
    vmm.unset_finalizer(obj2.objref)
}


pub const (
    ffity_void = 0
    ffity_pointer = 1
)

